Repository navigation
feat(renderer): status pages before Astro (RFC-0043 PR 1) - #162
Merged
Merged
Conversation
A site that cannot show its pages answers with one small, translated,
self-contained page instead of plain text: not-published (503,
Retry-After 3600, noindex), preview-expired (410), unavailable (503) and
error (500, reference id only), every one no-store with its own CSP and
under 8 KB. The Site Worker entry decides the state before Astro runs
(siteState, memoised per request), answers GET /__cloudhal/health with
{ ok, state } in the admin's shape, and turns any failure into the error
page. The catalog covers every registered Studio language through the
i18n exchange flow (a second source of scripts/i18n-exchange.ts; only
en.ts is hand-edited) with simple ICU arguments only. A theme may export
statusPage from src/theme.ts, loaded lazily through
virtual:cloudhal/theme-hooks and falling back to the built-in page on a
throw, a slow answer or an oversized document. cloudhal dev draws each
state at /__cloudhal/status/<state>. The blog starter's routes use the
renderer's fallbacks. An invalid preview link now answers 410 instead of
the published page.
Refs #117
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
The deploy test expected the runtime's default 500 for a site Worker whose site row is not pushed yet; the entry now answers 503 with the `unavailable` status page (Retry-After 300, no-store), and the pushed but never published site answers the `not-published` page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
CodeQL (js/polynomial-redos) flagged the trailing-slash regex in the status page's locale choice: it runs on request text. A linear loop replaces it, with a unit case of 200,000 slashes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com>
This was referenced Nov 8, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Rollout PR 1 of RFC-0043 (status pages and maintenance mode), from #117: a site that cannot show its pages answers with one small, translated, self-contained page instead of plain text. Self-contained: no contract, no writer, no maintenance yet.
@cloudhal/astro-renderer/status-page: the purestatusPage(state, input) → Response(statusPlainfor machine paths,statusPageHtmlfor hooks) with the closed state setnot-published(503,Retry-After: 3600,noindex),maintenance(503,Retry-Afterto the end),unavailable(503, 300),error(500, reference id only) andpreview-expired(410,noindex). Every response isno-storeon browser and edge withx-cloudhal-stateand the page's own CSP (default-src 'none'); the document stays under 8 KB in every state and language and uses logical properties only.maintenanceandpreview-expiredmay carry the published site's typed theme tokens (brand, surface/text when they contrast, the font pairing).siteStateinruntime-workerd.ts, memoised per request so Astro'sgetSourcereads the pointer once), withGET /__cloudhal/healthanswering{ "ok": true, "state": "published" | "not-published" }(the admin's/api/healthshape plusstate), a top-level catch and Astro 5xx → theerrorpage. Status pages go through the existing render events.src/status-strings/) covers every registered Studio language:en.tsis hand-edited, the other 18 come frompnpm i18n:import(the exchange script gains a second source,status.jsonper locale, same hash guard), andcatalog.tsis generated from the registry. Messages use simple ICU arguments only, so no compiler ships in the Worker. The 18 translations are AI drafts awaiting review, as Studio's.statusPageexport insrc/theme.ts(StatusPageHookfrom@cloudhal/astro-renderer/status-page) returns a document only; it falls back to the built-in page onundefined, a throw, a non-string, > 64 KB or > 50 ms (logged once per state) and never runs forerror. Loaded lazily throughvirtual:cloudhal/theme-hooks(import("/src/theme.ts")), so old theme copies get the built-in page.cloudhal devdraws each state on demand at/__cloudhal/status/<state>(?locale,?page=coming-soon,?message,?until) through the same module and the project's hook.statusPage/statusPlainas their fallback instead of the plain 503s.Not in this PR (RFC rollout PR 2):
cloudhal build --publishedwriting the status page as the static output for the Docker self-host path, the Caddyfile 503 block and the self-host guide.Tests run (only the touched files, per the owner's machine rule):
packages/astro-renderer/test/status-page.test.ts(29),packages/cli/test/cloudflare-status.test.ts(5, workerd: the page before the first publish with the theme hook, machine paths, health, preview-expired, the publish flip),cloudflare-multisite.test.ts(5, with the new two-host check: an unpublished site on its own host gets its page withno-storeandVary: Host, its siblings serve),cloudflare.test.ts+packaging.test.ts(20),dev-status.test.ts(1),apps/studio/test/i18n-messages.test.ts(9).pnpm lint,pnpm typecheck,pnpm size:check,pnpm design:lintandnode scripts/i18n-exchange.ts checkgreen.Checklist
pnpm checkandcargo test --workspacepass locally (not run: only the touched test files, lint, typecheck, size and design lint; CI runs the rest)pnpm genwas run and generated files are committed (docs/adr/0001) — no contract changepnpm changesetaddedgit commit -s, DCO)🤖 Generated with Claude Code